package net.cyndeline.entitySystem.defaultImpl

import net.cyndeline.entitySystem.EventSystem
import net.cyndeline.entitySystem.EntityManager
import net.cyndeline.entitySystem.EntityRef
import net.cyndeline.entitySystem.Component
import scala.collection.immutable.List
import net.cyndeline.entitySystem.PersistableEntityManager
import net.cyndeline.entitySystem.IdManager
import net.cyndeline.entitySystem.event.AddComponentEvent
import net.cyndeline.entitySystem.event.RemovedComponentEvent
import net.cyndeline.entitySystem.event.ChangedComponentEvent
import language.postfixOps
import com.escalatesoft.subcut.inject.BindingModule
import com.escalatesoft.subcut.inject.Injectable
import net.cyndeline.entitySystem.event.AddComponent
import net.cyndeline.entitySystem.event.ChangedComponent
import net.cyndeline.entitySystem.event.RemovedComponent
import net.cyndeline.entitySystem.ComponentTable
import net.cyndeline.EntityKey
import scala.collection.mutable.Map

/**
 * Central manager for the entity system. Entities delegates most of their
 * work here rather than directly performing work whenever they have their 
 * methods called.
 */
class DefaultEntityManager(implicit val bindingModule: BindingModule) extends EntityManager with PersistableEntityManager with Injectable {
  private val idManager = injectOptional [IdManager] getOrElse new DefaultIdManager
  private val store = injectOptional [ComponentTable] getOrElse new DefaultComponentTable
  private var eventSys: Option[EventSystem] = injectOptional[EventSystem]
  private var entityCache: Map[Int, EntityRef] = Map()
  private var keyEntities: Map[EntityKey, EntityRef] = Map()
  
  override def clear {
    store.clear
    idManager.clear
    eventSys = None
    entityCache = Map()
  }
  
  override def create: EntityRef = createEntityRef(idManager getNext)
  
  override def create(components: Component*): EntityRef = create(components.toIterator)
  
  override def create(components: Iterator[Component]): EntityRef = {
    val entity = create
    
    for (component <- components) {
      store.put((entity.id), component)
    }
    
    eventSystem match {
      case Some(system) => {
        val addEvent = injectOptional[AddComponentEvent] getOrElse new AddComponent
        addEvent.components = components.toList
        system.deliverEvent(entity, addEvent)
      }
      case None => {}
    }
    
    entity
  }
  
  override def componentCount(componentClass: Class[_ <: Component]): Int = 
    store.getComponentCount(componentClass)
  
  override def eventSystem = eventSys
  
  override def setEventSystem(evntSys: EventSystem) = eventSys = Option(evntSys)
  
//  override def iterateComponents[A <: Component](componentClass: Class[A]): Iterator[(EntityRef, Component)] = {
//    val idToComponent: Option[Map[Int, Component]] = store.componentMapping(componentClass)
//    val mapping = idToComponent getOrElse Map[Int, Component]()
//    var storage = List[(EntityRef, Component)]()
//    mapping.foreach {case (entiRefId, component) =>
//      storage = storage :+ (createEntityRef(entiRefId), component)
//    }
//    
//    storage.toIterator
//  }
//  
  override def createEntityWithId(entityId: Int): EntityRef = {
    if (!(idManager isAvailable entityId)) {
      createEntityRef(entityId)
      
    } else {
      EntityRef.NULL
    }
  }
  
  override def createEntityWithId(entityId: Int, components: Iterator[Component]): EntityRef = {
    
    /* createEntityRef(id) will check if the id is occupied. */
    if (!(idManager isAvailable entityId)) {
      val entity = createEntityRef(entityId)
      
      for (component <- components) {
        store.put(entityId, component)
      }
      
      if (eventSystem isDefined) {
        val system = (eventSystem get)
        val addEvent = inject[AddComponentEvent]
        addEvent.components = components.toList
        system.deliverEvent(entity, addEvent)
      }
      
      entity
      
    } else {
      EntityRef.NULL
    }
  }
  
  override def activeEntities: Int = entityCache size
  
  /* Persisting entity manager methods: */
  override def nextId = idManager viewNext
  override def setNextId(id: Int) = idManager setNext id
  override def freedIds = idManager.getFreeIds.toList
  /* End */
  
  override def iteratorEntities: Iterator[EntityRef] = {
    new EntityIterator(entityCache.keys.iterator)
  }
//  
//  override def iteratorEntities(componentClasses: Class[Component]*): Iterator[EntityRef] = {
//    if (componentClasses.isEmpty) {
//      return iteratorEntities
//      
//    } else {
//      var entityResult = Set[Int]()
//      
//      for (compClass <- componentClasses) {
//        val entityComponentPairs = store.componentMapping(compClass)
//        val map = entityComponentPairs getOrElse Map()
//        
//        for ((entity, component) <- map) {
//          entityResult = (entityResult + entity)
//        }
//      }
//      
//      new EntityIterator(entityResult.iterator)
//    }
//  }
  
  override def keyEntity(key: EntityKey) = {
    keyEntities.get(key)
  }
  
  override def addKeyEntity(key: EntityKey, entity: EntityRef) {
    keyEntities += (key -> entity)
  }
  
  override def hasComponent(entityId: Int, componentClass: Class[_ <: Component]): Boolean = {
    store.get(entityId, componentClass) isDefined
  }
  
  override def iterateComponents(entityId: Int): Iterator[Component] = {
    store.iterateComponents(entityId)
  }
  
  override def destroy(entityId: Int) {
    val entity = createEntityRef(entityId)
    
    if (!(eventSystem isEmpty)) {
      val system = (eventSystem get)
      val removedEvent = injectOptional [RemovedComponentEvent] getOrElse new RemovedComponent
      system.deliverEvent(entity, removedEvent)
    }
    
    entity.invalidate // Set id to null
    idManager.freeId(entityId)
    store remove entityId
    entityCache -= entityId
  }
  
  override def getComponent[A <: Component](entityId: Int, componentClass: Class[A]): Option[_ <: Component] = {
    store.get(entityId, componentClass)
  }
  
  override def addComponent[T <: Component](entityId: Int, component: T): T = {
    val oldComponent = store.put(entityId, component)
    
    if (eventSystem.isDefined) {
      val system = (eventSystem get)
      
      if (oldComponent isEmpty) {
        val addEvent = injectOptional[AddComponentEvent] getOrElse new AddComponent
        addEvent.components = component
        system.deliverEvent(createEntityRef(entityId), addEvent);
        
      } else {
        val changeEvent = injectOptional[ChangedComponentEvent] getOrElse new ChangedComponent
        changeEvent.component = component
        system.deliverEvent(createEntityRef(entityId), changeEvent);
      }
    }
    
    component
  }
  
  override def removeComponent(entityId: Int, componentClass: Class[_ <: Component]) {
    val component = store.get(entityId, componentClass)
    
    component match {
      case Some(comp) => {
        if (eventSystem isDefined) {
	        val system = (eventSystem get)
	        val removedEvent = injectOptional [RemovedComponentEvent] getOrElse new RemovedComponent
	        removedEvent.component = Option(comp)
	        system.deliverEvent(createEntityRef(entityId), removedEvent)
        }

        store.remove(entityId, componentClass)
      }
      case None => {}
    }
  }
  
  def copy(other: EntityRef): EntityRef = {
      if (!other.exists) {
        return EntityRef.NULL
      }
      
      //val components = for (c <- other.iterateComponents) yield { c.copy }
      //return create(components)
      return create
    }
  
  private def createEntityRef(entityId: Int): EntityRef = {
    if (entityId == IdManager.NULL_ID) {
      EntityRef.NULL
    }
    
    val existing = entityCache get entityId
    if (existing isDefined) {
      return existing get
    }
    
    val newEntity = injectOptional[EntityRef] getOrElse new DefaultEntityRef
    newEntity.id = entityId
    newEntity.entityManager = this
    idManager setNext entityId + 1
    entityCache = entityCache + (entityId -> newEntity)
    newEntity
  }
  
  private class EntityIterator(idIterator: Iterator[Int]) extends Iterator[EntityRef] {
    def hasNext = idIterator hasNext
    def next = createEntityRef(idIterator.next)
    def remove = throw new UnsupportedOperationException("This iterator is only used to create EntityRef's from entity id's.")
  }
}